<template>
  <div class="role-right-dialog">
    <el-dialog
      :title="titleName"
      width="60%"
      top="6vh"
      center
      :visible.sync="visible"
      :before-close="modalClose"
      @open="modelOpen"
      :close-on-click-modal="false">
      <el-row class="row-box">
        <el-col :span="6" class="left-col">
          <div class="col-title">
            菜单配置<small>(角色可见菜单列表)</small>
          </div>
          <div class="left-treebox" ref="menuTreeScroll" v-loading="menuLoading">
            <el-tree
              :data="menuTreeData"
              show-checkbox
              node-key="id"
              ref="menuTree"
              highlight-current
              :props="menuProps"
              :indent="30"
              :default-expanded-keys="menuNodeKeys"
              @current-change="menuCurrentChange"
              @check="menuCheck">
            </el-tree>
          </div>
        </el-col>
        <el-col :span="18">
          <div class="col-title">接口配置<small>(角色菜单可访问的接口列表)</small></div>
          <div class="right-checkbox" ref="rightTreeScroll" v-loading="rightLoading">
            <div class="checkbox-group" v-for="(item, index) in rightTreeData" :key="index">
              <el-checkbox style="font-weight: bold;" :indeterminate="item.isIndeterminate" v-model="item.selectAll" @change="val => handleCheckAllChange(val, index)">{{ item.label }}</el-checkbox>
              <el-checkbox-group v-model="item.selected" @change="val => handleCheckBox(val, index)">
                <el-checkbox v-for="(items, indexs) in item.childs" :label="items.id" :key="item.module + indexs">{{ items.label }}</el-checkbox>
              </el-checkbox-group>
            </div>
          </div>
        </el-col>
      </el-row>
      <div slot="footer">
        <!-- <el-button type="cancel" @click="modalClose">取 消</el-button> -->
        <!-- <el-button type="primary" @click="formSubmit" :loading="loading">确 定</el-button> -->
        <el-button type="primary" @click="modalClose" :loading="loading">确 定</el-button>
      </div>
    </el-dialog>
  </div>
</template>

<script>
import util from '@/libs/util'
export default {
  name: 'roleMenuDialog',
  props: {
    visible: {
      type: Boolean,
      default: false,
    },
    modelData: {
      type: Object,
      default: () => {
        return {};
      },
    },
  },
  data() {
    return {
      titleName: '角色权限配置',
      menuSearchForm: {
        pageIndex: 1,
        pageSize: 9999
      },
      rightSearchForm: {
        pageIndex: 1,
        pageSize: 999,
        module: ''
      },
      // menuList: [],
      menuTreeData: [],
      menuTreeLeaf: [], // 菜单树叶子节点数组
      rightInitData: [],
      rightTreeData: [],
      loading: false,
      menuLoading: false,
      rightLoading: false,
      menuProps: {
        children: 'children',
        label: 'cname',
      },
      rightProps: {
        children: 'childs',
        label: 'label'
      },
      menuNodeKeys: [], // 左侧菜单树默认展开节点
      rightNodeKeys: [],
      isIndeterminate: false, // 半选状态
      currentMenuKey: '', //左侧菜单树 当前选中菜单
      interfacesChecked: [], // 已选择接口数组
    };
  },
  created() {},
  mounted() {},
  methods: {
    // 对话框打开
    async modelOpen() {
      await this.loadMenuList();
      await this.loadRightList();
      await this.loadRoleMenuList();
    },
    // 加载菜单列表&加载角色已有菜单
    async loadMenuList() {
      this.menuLoading = true;
      try {
        // 加载菜单列表
        let menuArr = await this.$api.menu.findMenuList(this.menuSearchForm);
        // this.menuList = menuArr.value;
        // 处理菜单树型结构
        let menuTreeInfo = util.menuTree(menuArr.value, true);
        this.menuTreeData = menuTreeInfo.treeData;
        this.menuTreeLeaf = menuTreeInfo.leafNodeIds;
        this.$nextTick(() => {
          if(this.menuTreeData[0] && this.menuTreeData[0].id) {
            this.handleMenuExpandedKeys(this.menuTreeData); // 获取左侧菜单树展开节点
            this.$refs.menuTree.setCurrentKey(this.menuTreeData[0].id); // 默认设置第一个节点高亮
            this.currentMenuKey = this.menuTreeData[0].id;
          }
        })
        this.menuLoading = false;
      } catch (error) {
        console.log(error);
        this.menuLoading = false;
      }
    },

    async loadRightList() {
      this.rightLoading = true;
      try {
        // 加载菜单列表
        let rightArr = await this.$api.right.findRightList(this.rightSearchForm);
        // 处理菜单树型结构
        this.rightInitData = util.rightTree(rightArr.value);
        this.rightTreeData = JSON.parse(JSON.stringify(this.rightInitData));
        this.rightLoading = false;
      } catch(error) {
        console.log(error);
        this.rightLoading = false;
      }
    },

    async loadRoleMenuList() {
      try {
        // 加载角色已有菜单
        let param = {
          pageIndex: 1,
          pageSize: 9999,
          roleId: this.modelData.id,
        };
        this.$api.roleMenu.findRoleMenuList(param)
        .then(async (res) => {
          let keys = this.handleTreeChecked(res.value);
          // 获取菜单树已选中叶节点 并勾选
          let renderMenuTreeKeys = util.renderMenuTreeKeys(this.menuTreeLeaf, keys.menuKeys)
          this.$refs.menuTree.setCheckedKeys(renderMenuTreeKeys);
          // 右侧复选框组默认显示第一个菜单的接口列表
          await this.renderRightChecked(this.menuTreeData[0])
          // 设置滚动条
          await this.setScrollTop();
        })

      } catch(error) {
        console.log(error)
      }

    },

    // 左侧菜单树 默认展开第一级菜单
    handleMenuExpandedKeys(menuArr) {
      if(menuArr.length > 0) {
        let keys = [];
        menuArr.forEach(item => {
          keys.push(item.id);
        });
        this.menuNodeKeys = keys;
      }
    },

    // 处理菜单树已选节点
    handleTreeChecked(roleMenuArr) {
      let keys = {
        menuKeys: [],
        rightKeys: []
      }
      for (let i = 0, len = roleMenuArr.length; i < len; i++) {
        keys.menuKeys.push(roleMenuArr[i].menuId);
        if(roleMenuArr[i].interfaces) {
          var interfaces = roleMenuArr[i].interfaces.split(',');
          interfaces.forEach(item => {
            keys.rightKeys.push(item)
          });
        }
      }
      // this.menuNodeKeys = keys.menuKeys; // 左侧菜单树 展开所有已选节点
      this.rightNodeKeys = keys.rightKeys;
      return keys;
    },

    // 当前选中节点变化时触发的事件
    async menuCurrentChange(data, node) {
      // 判断是否点击相同节点 不同则查询接口 更新选择框组
      if(this.currentMenuKey != data.id) {
        this.$nextTick(() => {
          this.renderRightChecked(data);
        })
      }
    },

    // 渲染已选择接口
    async renderRightChecked(data) {
      if(!data.id) {
        return;
      }
      this.currentMenuKey = data.id;
      this.rightTreeData = JSON.parse(JSON.stringify(this.rightInitData)); // 重置右侧选择框组
      this.interfacesChecked = this.$options.data().interfacesChecked; // 重置已选择接口数组
      try {
        // 查接口 获取已选择接口
        let param = {
          pageIndex: 1,
          pageSize: 9999,
          roleId: this.modelData.id,
          menuId: data.id
        }
        let info = await this.$api.roleMenu.findRoleMenuList(param);
        if(info.value.length > 0 && info.value[0].interfaces) {
          this.interfacesChecked = info.value[0].interfaces.split(',');
          // 根据已选择接口 初始化右侧选择框组
          this.rightTreeData = util.renderRightData(this.rightTreeData, this.interfacesChecked);
        }
      } catch(err) {
        this.$message.error(err || '菜单配置接口初始化失败')
      }
    },

    // 当复选框被点击的时候触发 勾选树节点 提交数据
    async menuCheck(data, node) {
      let currentNode = this.$refs.menuTree.getNode(data.id);
      this.$refs.menuTree.setCurrentKey(data.id); // 复选框选中时 自动设置高亮
      this.currentMenuKey = data.id;
      try {
        if(currentNode.checked) {
          // 勾选 新增
          let param = {
            roleId: this.modelData.id,
            menuId: this.$refs.menuTree.getCurrentKey(),
            checkAll: 1
          }
          this.$api.roleMenu.addRoleMenu(param)
            .then((res) => {
              // 菜单添加成功 重置右侧选择框组
              this.rightTreeData = JSON.parse(JSON.stringify(this.rightInitData));
            })
            .catch((err) => {
              console.log('add err', err);
            })
        } else {
          // 取消复选框勾选 获取角色权限id 删除
          let param = {
            pageIndex: 1,
            pageSize: 1,
            roleId: this.modelData.id,
            menuId: this.$refs.menuTree.getCurrentKey()
          }
          let info = await this.$api.roleMenu.findRoleMenuList(param)
          if(info.value.length > 0) {
            this.$api.roleMenu.delRoleMenu(info.value[0].id)
            .then((res) => {
              // 删除成功 重置右侧选择框组
              this.rightTreeData = JSON.parse(JSON.stringify(this.rightInitData));
            })
            .catch((err) => {
              currentNode.checked = !currentNode.checked;
            })
          }
        }
      } catch(err) {
        console.log('err', err);
      }
    },

    // 接口复选框组 全选框监听
    handleCheckAllChange(checked, index) {
      let currentNodeChecked = this.checkMenu();
      if(currentNodeChecked) {
        if(checked) {
          let arr = [];
          this.rightTreeData[index].childs.forEach(element => {
            arr.push(element.id);
          });
          this.rightTreeData[index].selected = arr;
        } else {
          this.rightTreeData[index].selected = [];
        }
        this.rightTreeData[index].isIndeterminate = false;
        this.addInterfaces(currentNodeChecked);
      }  else {
        // 当前菜单节点未勾选时 不改变复选框原值
        this.rightTreeData[index].selectAll = !checked;
      }
    },

    // 接口复选框组 复选框监听
    handleCheckBox(checked, index) {
      let currentNodeChecked = this.checkMenu();
      if(currentNodeChecked) {
        let childsCount = this.rightTreeData[index].childs.length;
        let checkCount = checked.length;
        this.rightTreeData[index].selectAll = childsCount === checkCount;
        this.rightTreeData[index].isIndeterminate = !(childsCount === checkCount) && checkCount > 0 && checkCount < childsCount;
        this.addInterfaces(currentNodeChecked);
      } else {
        // 当前菜单节点未勾选时 不改变复选框原值
        checked = checked.splice(checked.length - 1);
      }
    },

    // 检查当前选中的菜单节点是否勾选
    checkMenu() {
      let currentKey = this.$refs.menuTree.getCurrentKey();
      let currentNode = this.$refs.menuTree.getNode(currentKey);
      // checked=true => 全选 indeterminate=true => 半选
      if(!currentNode.checked) {
        if(currentNode.isLeaf) {
          // 叶节点 未选中
          this.$message.error('当前菜单节点未被勾选');
          return false;
        } else {
          if(!currentNode.indeterminate) {
            // 非叶节点 非部分选中
            this.$message.error('当前菜单节点未被勾选');
            return false;
          } else {
            return 'indeterminate';
          }
        }
      } else {
        return 'checked';
      }
    },

    /**
     * 提交配置接口
     * @param {String} currentNodeChecked 当前选中菜单的勾选状态 checked全选 indeterminate半选
     */
    addInterfaces(currentNodeChecked) {
      let checkAll = currentNodeChecked == 'checked' ? 1 : 0;
      let interfaces = [];
      // 遍历获取当前菜单的所有选中接口
      this.rightTreeData.forEach(right => {
        interfaces = interfaces.concat(right.selected);
      });
      let data = {
        roleId: this.modelData.id,
        menuId: this.$refs.menuTree.getCurrentKey(),
        interfaces: interfaces.length ? interfaces.join(',') : '',
        checkAll: checkAll // 是否全选
      }
      this.$api.roleMenu.addRoleMenu(data)
        .then((res) => {
          // console.log('update res', res);
        })
        .catch((err) => {
          console.log('err', err);
        })
    },

    // 表单提交
    formSubmit() {
      this.loading = true;
      let nodes = this.$refs.menuTree.getCheckedKeys(true, false).toString();
      this.modelData['rights'] = nodes;
      this.$api.userRole
        .updateRole(this.modelData)
        .then((res) => {
          this.loading = false;
          this.$message.success('赋权成功');
          this.modalClose();
        })
        .catch((err) => {
          this.loading = false;
          console.log(err);
        });
    },

    // 设置菜单树滚动条回到顶部
    setScrollTop() {
      let initialNode = this.$refs.menuTreeScroll;
      let pos = initialNode.scrollTop;
      if (pos > 0) {
        initialNode.scrollTop = 0;
      }
    },

    // 对话框关闭
    modalClose() {
      this.openNodeKeys = [];
      this.$emit('update:visible', false); // 直接修改父组件的属性
    },
  },
};
</script>

<style lang="scss" scoped>

.role-right-dialog {
  .row-box {
    .col-title {
      // text-align: center;
      font-size: 16px;
      font-weight: bold;
      // line-height: 30px;
      padding: 10px;
      small {
        font-weight: normal;
      }
    }
  }
  .left-treebox {
    display: flex;
    height: 35rem;
    padding: 1rem;
    overflow-y: scroll;
    // border: 1px solid #999;
    // border-right: none;
    .el-tree {
      width: 100%;
      .el-tree-node__label {
        width: auto;
        float: right;
        font-family: “Trebuchet MS”, Arial, Helvetica, sans-serif;
        font-size: 18px;
        color: #3e4655;
        line-height: 25px;
        letter-spacing: 1px;
      }
      .el-tree-node__content {
        height: 34px;
        .el-tree-node__expand-icon {
          font-size: 28px;
        }
        .el-checkbox {
          .el-checkbox__input {
            .el-checkbox__inner {
              width: 20px;
              height: 20px;
            }
            .el-checkbox__inner::after {
              height: 11px;
              left: 6px;
              width: 5px;
            }
            .el-checkbox__inner::before {
              top: 8px;
            }
          }
        }
      }
      ::v-deep.el-tree-node__children {
        overflow: visible;
      }
    }
  }
  .right-checkbox {
    height: 35rem;
    padding: 1rem;
    overflow-y: scroll;

    .el-checkbox {
      line-height: 28px;
    }

    .checkbox-group {
      margin-bottom: 15px;
    }
  }

  /* 一开始隐藏 */
  .left-treebox::-webkit-scrollbar {
    display: none;
  }

  /* 鼠标经过再显示 */
  .left-treebox:hover::-webkit-scrollbar {
    display: block;
    width: 0.375rem;
    height: 0.375rem;
    background-color: #fff;
    border-radius: 0.625rem;
  }

  .left-treebox::-webkit-scrollbar-thumb {
    background-color: rgba(134, 191, 250, 0.7);
    border-radius: 0.625rem;
  }
}

</style>
